home *** CD-ROM | disk | FTP | other *** search
Text File | 1989-05-10 | 5.5 KB | 154 lines | [TEXT/PERL] |
- ;;;This file contains all kinds of wonderful "stream" stuff from Abelson and
- ;;;Sussman. The problem is that "stream" means something else within the
- ;;;context of Common Lisp and therefore within Pearl Lisp, so I call 'em
- ;;;"lazy-lists," which to me makes more sense anyway.
-
- ;;Define the empty lazy list:
- (defconstant the-empty-lazy-list '())
-
- ;;How do we know if a lazy-list is empty?
- (defun empty-lazy-list-p (lazy-list)
- (eq the-empty-lazy-list lazy-list))
-
- ;;This function optimizes DELAY so that the function created is only called once.
- (defun memoize (fun)
- (let ((already-evaled nil) (value nil))
- #'(lambda ()
- (if already-evaled
- value
- (prog1
- (setf value (funcall fun))
- (setf already-evaled t))))))
-
- ;;Here is the DELAY macro:
- (defmacro delay (thing)
- `(memoize #'(lambda () ,thing)))
-
- ;;Here is the FORCE function:
- (defun force (promise)
- (funcall promise))
-
- ;;Here is our lazy-list CONStructor:
- (defmacro lazy-cons (thing lazy-list)
- `(cons ,thing (delay ,lazy-list)))
-
- ;;Here are LAZY-CAR and LAZY-CDR:
- (defun lazy-car (lazy-list)
- (car lazy-list))
-
- (defun lazy-cdr (lazy-list)
- (force (cdr lazy-list)))
-
- ;;This is to lazy lists what Common Lisp's APPEND is to normal lists.
- (defun append-lazy-lists (l1 l2)
- (if (empty-lazy-list-p l1)
- l2
- (lazy-cons (lazy-car l1)
- (append-lazy-lists (lazy-cdr l1) l2))))
-
- ;;This is a nice, generic accumulation function that takes a combiner function
- ;;(usually #'+ or #'cons or something like that), an initial value (typically
- ;;0 or 1 for numeric accumulations or '() for lists) and some lazy-list to
- ;;apply all of this to.
- (defun accumulate (combiner initial-value lazy-list)
- (if (empty-lazy-list-p lazy-list)
- initial-value
- (funcall combiner (lazy-car lazy-list)
- (delay (accumulate combiner
- initial-value
- (lazy-cdr lazy-list))))))
-
- ;;This function prevents infinite recursion when accumulating infinite lazy-lists.
- (defun interleave (l1 l2)
- (if (empty-lazy-list-p l1)
- (force l2)
- (lazy-cons (lazy-car l1)
- (interleave (force l2) (delay (lazy-cdr l1))))))
-
- ;;This handy thing uses ACCUMULATE to flatten a lazy-list of lazy-lists.
- (defun flatten (lazy-list)
- (accumulate #'interleave the-empty-lazy-list lazy-list))
-
- ;;This maps some proc across every element of some lazy-list.
- (defun lazy-map (proc lazy-list)
- (if (empty-lazy-list-p lazy-list)
- the-empty-lazy-list
- (lazy-cons (funcall proc (lazy-car lazy-list))
- (lazy-map proc (lazy-cdr lazy-list)))))
-
- ;;This returns the lazy-list that contains all items that, when passed to pred,
- ;;return something non-NIL.
- (defun filter (pred lazy-list)
- (cond ((empty-lazy-list-p lazy-list) the-empty-lazy-list)
- ((funcall pred (lazy-car lazy-list))
- (lazy-cons (lazy-car lazy-list)
- (filter pred (lazy-cdr lazy-list))))
- (t (filter pred (lazy-cdr lazy-list)))))
-
- ;;This is an awful lot like LAZY-MAP, except that it doesn't accumulate its
- ;;results, which is a fancy way of saying that you use LAZY-MAP if you need
- ;;a function result and FOR-EACH if you need side-effects.
- (defun for-each (proc lazy-list)
- (if (empty-lazy-list-p lazy-list)
- 'done
- (progn (funcall proc (lazy-car lazy-list))
- (for-each proc (lazy-cdr lazy-list)))))
-
- ;;Flattening the result of lazy-mapping is so useful and so common that there's
- ;;a whole separate function for it.
- (defun flatmap (f s)
- (flatten (lazy-map f s)))
-
- ;;Sometimes (ok, rarely) it's nice to convert a list to a lazy-list:
- (defun lazy-list (list)
- (if (null list)
- the-empty-lazy-list
- (lazy-cons (car list) (lazy-list (cdr list)))))
-
- ;;This is the tricky one. The COLLECT macro makes nested mappings a tad easier
- ;;than they would be otherwise, but this is the most complex macro I've ever
- ;;had to write. Here goes nothing:
- (defmacro collect (result pairs &optional (restriction t))
- (let ((vars (mapcar #'car pairs))
- (sets (mapcar #'cadr pairs))
- (lets (genlets pairs)))
- `(lazy-map #'(lambda (tuple)
- (let ,lets
- ,result))
- (filter #'(lambda (tuple)
- (let ,lets
- ,restriction))
- ,(genmaps vars sets)))))
-
- ;;Given a list of pairs, this creates a let body based on tuple.
- (defun genlets (pairs)
- (do ((i (1- (length pairs)) (1- i))
- (result '() (cons (cons (car (nth i pairs)) (list (list 'nth i 'tuple))) result)))
- ((< i 0) result)))
-
- ;;This beast generates the flatmap/lazy-map sequence for the vars and sets.
- (defun genmaps (vars sets)
- (labels ((genmaps-1 (vars sets depth)
- (if (null (cdr sets))
- `(lazy-map #'(lambda (,(car (last vars)))
- (list ,@vars))
- ,(car sets))
- `(flatmap #'(lambda (,(nth depth vars))
- ,(genmaps-1 vars (cdr sets) (1+ depth)))
- ,(car sets)))))
- (genmaps-1 vars sets 0)))
-
- (defconstant ones (lazy-cons 1 ones))
-
- (defun add-lazy-lists (l1 l2)
- (cond ((empty-lazy-list-p l1) l2)
- ((empty-lazy-list-p l2) l1)
- (t
- (lazy-cons (+ (lazy-car l1) (lazy-car l2))
- (add-lazy-lists (lazy-cdr l1) (lazy-cdr l2))))))
-
- (defconstant integers (lazy-cons 1 (add-lazy-lists ones integers)))
-
- (defun scale-lazy-list (c lazy-list)
- (lazy-map #'(lambda (x) (* x c)) lazy-list))
-